package cn.piggy.mail.component;

import cn.piggy.mail.config.MailConfigParam;
import cn.piggy.mail.constant.MailType;
import cn.piggy.mail.model.query.DateQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.activation.DataHandler;
import javax.annotation.Resource;
import javax.mail.*;
import javax.mail.internet.*;
import javax.mail.search.AndTerm;
import javax.mail.search.ComparisonTerm;
import javax.mail.search.ReceivedDateTerm;
import javax.mail.search.SearchTerm;
import javax.mail.util.ByteArrayDataSource;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Comparator;
import java.util.Date;
import java.util.Properties;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Component
public class MailExecutor {

    private final Logger logger = LoggerFactory.getLogger(MailExecutor.class);
    @Resource
    private MailConfigParam mailConfigParam;

    public void handleMail(DateQuery dateQuery) throws MessagingException, ParseException, IOException {
        Properties props = new Properties();
        props.setProperty("mail.debug", "false");
        props.setProperty("mail.imap.host", "mail.bobfintech.com.cn");
        props.setProperty("mail.imap.port", "143");
//        props.setProperty("mail.imap.ssl.enable", "true");
        props.setProperty("mail.imap.partialfetch", "false");
        props.setProperty("mail.store.protocol", "imap");
        Session session = Session.getInstance(props);
        Store store = session.getStore("imap");
        store.connect(mailConfigParam.getUsername(), mailConfigParam.getPassword());
        // Sent 发信箱 / INBOX 收信箱
        Message[] messages1 = getMessages(store, dateQuery.getStartTime(), dateQuery.getEndTime(), MailType.Sent);
        executeMessages(messages1);
        zipFile(messages1);
        Message[] messages2 = getMessages(store, dateQuery.getStartTime(), dateQuery.getEndTime(), MailType.INBOX);
        executeMessages(messages2);
        zipFile(messages2);
        store.close();

    }

    private void executeMessages(Message[] messages) throws MessagingException, IOException {
        for(Message m : messages){
            MimeMessage message = (MimeMessage) m;
            savePart(message, mailConfigParam.getSavePath());
            StringBuilder stringBuilder = new StringBuilder();
            getMailContent(message, stringBuilder);
            logger.info(" content --> "+ stringBuilder);
            String receiver = getReceiver(message);
            logger.info(" receiver --> "+ receiver);
            String sender = getSender(message);
            logger.info(" sender --> "+ sender);
            String subject = message.getSubject();
            logger.info(" subject --> "+ subject);
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            Date receivedDate = message.getReceivedDate();
            String receive = simpleDateFormat.format(receivedDate);
            logger.info(" receive --> "+ receive);
            Date sentDate = message.getSentDate();
            String send = simpleDateFormat.format(sentDate);
            logger.info(" send --> "+ send);
        }
    }

    private Message[] getMessages(Store store, String startTime, String endTime, String type) throws MessagingException, ParseException {
        Folder folder = store.getFolder(type);
        folder.open(Folder.READ_ONLY);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date start = simpleDateFormat.parse(startTime);
        Date end = simpleDateFormat.parse(endTime);
        SearchTerm startTerm = new ReceivedDateTerm(ComparisonTerm.GE, start);
        SearchTerm endTerm = new ReceivedDateTerm(ComparisonTerm.LE, end);
        AndTerm andTerm = new AndTerm(startTerm, endTerm);
        Message[] messages = folder.search(andTerm);
//        Message[] messages = folder.getMessages();
        if(messages==null){
            messages = new Message[0];
        }
        return messages;
    }

    private boolean savePart(Part part, String path) throws MessagingException, IOException {
        boolean isExist = false;
        if(part.isMimeType("multipart/*")){
            Multipart multipart = (Multipart)part.getContent();
            for (int i=0; i<multipart.getCount(); i++){
                BodyPart bodyPart = multipart.getBodyPart(i);
                if(bodyPart.isMimeType("multipart/*")){
                    boolean b = savePart(bodyPart, path);
                    logger.info(" savePart --> {}", b);
                }else{
                    String filename = bodyPart.getFileName();
                    if(filename!=null){
                        filename = MimeUtility.decodeText(filename);
                        saveFile(path+filename, bodyPart);
                        isExist = true;
                    }
                }
            }
        }
        return isExist;
    }

    private void saveFile(String filepath, BodyPart bodyPart) throws MessagingException, IOException {
        InputStream inputStream = bodyPart.getInputStream();
        byte[] bytes = new byte[10240];
        FileOutputStream fileOutputStream = new FileOutputStream(filepath);
        int ii;
        while ((ii = inputStream.read(bytes, 0, bytes.length)) != -1) {
            fileOutputStream.write(bytes, 0, ii);
        }
        fileOutputStream.close();
    }

    private void getMailContent(Part part, StringBuilder stringBuilder) throws MessagingException, IOException {
        if(part.isMimeType("text/plain")){
            String contentType = part.getContentType();
            if(contentType.contains("name")){
                return;
            }
            stringBuilder.append(part.getContent().toString());
        } else if(part.isMimeType("multipart/*")){
            Multipart multipart = (Multipart) part.getContent();
            int count = multipart.getCount();
            for(int i=0; i<count; i++){
                getMailContent(multipart.getBodyPart(i), stringBuilder);
            }
        }
    }

    private String getSender(MimeMessage message) throws MessagingException {
        Address[] from = message.getFrom();
        if(from!=null && from.length > 0){
            InternetAddress internetAddress = (InternetAddress) from[0];
            return internetAddress.toUnicodeString();
        }
        return "";
    }

    private String getReceiver(MimeMessage message) throws MessagingException {
        // to 收件人 / cc 抄送人 / bcc 密送人
        Address[] to = message.getRecipients(Message.RecipientType.TO);
        if(to!=null){
            StringBuilder stringBuilder = new StringBuilder();
            for(int i = 0; i< to.length; i++){
                InternetAddress address = (InternetAddress) to[i];
                String string = address.toUnicodeString();
                stringBuilder.append(string);
                if(i< to.length -1){
                    stringBuilder.append(", ");
                }
            }
            return stringBuilder.toString();
        }
        return "";
    }

    private void zipFile(Message[] messages) throws IOException, MessagingException {
        File file = new File(mailConfigParam.getSavePath());
        if(!file.exists()){
            boolean bool = file.mkdirs();
            logger.info("create --> "+bool);
        }
        String username = "000000";
        String zipFilename = username+"-"+UUID.randomUUID().toString().replace("-", "")+".zip";
        ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(Paths.get(mailConfigParam.getSavePath()+zipFilename)));
        for(int i=0; i< messages.length; i++){
            ZipEntry entry = new ZipEntry(String.format("%04d",i)+"-"+messages[i].getSubject()+".eml");
            zipOutputStream.putNextEntry(entry);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            messages[i].writeTo(byteArrayOutputStream);
            byte[] bytes = byteArrayOutputStream.toByteArray();
            zipOutputStream.write(bytes, 0, bytes.length);
        }
        zipOutputStream.close();
    }

    public void sedMail(String filepath, String filename, String receiver, String subject) throws MessagingException, IOException {
        Properties props = new Properties();
        props.setProperty("mail.smtp.port", "25");
        props.setProperty("mail.smtp.host", "");
        props.setProperty("mail.debug", "false");
        props.setProperty("mail.smtp.auth", "true");
        Session session = Session.getInstance(props, new MailAuthenticator(mailConfigParam.getUsername(), mailConfigParam.getPassword()));
        MimeMessage message = new MimeMessage(session);
        message.setFrom(new InternetAddress(mailConfigParam.getUsername()));
        message.setRecipient(Message.RecipientType.TO, new InternetAddress(receiver));
        message.setSubject(subject);
        byte[] bytes = Files.readAllBytes(Paths.get(filepath));
        int size = bytes.length / 1024 / 1024;
        if(size>50){
            throw new RuntimeException();
        }
        BodyPart bodyPart = new MimeBodyPart();
        bodyPart.setFileName(filename);
        DataHandler dataHandler = new DataHandler(new ByteArrayDataSource(bytes, "application/octet-stream"));
        bodyPart.setDataHandler(dataHandler);
        Multipart multipart = new MimeMultipart();
        multipart.addBodyPart(bodyPart);
        message.setContent(multipart);
        Transport.send(message);
    }

    private static class  MessageComparator implements Comparator<Message>{
        @Override
        public int compare(Message o1, Message o2) {
            try {
                if(o1.getReceivedDate().getTime()>= o2.getReceivedDate().getTime()){
                    return -1;
                }else {
                    return 1;
                }
            } catch (MessagingException e) {
                return 0;
            }
        }
    }

    private static class MailAuthenticator extends Authenticator{
        private final String username;
        private final String password;
        public MailAuthenticator(String username, String password) {
            this.username = username;
            this.password = password;
        }

        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(username, password);
        }
    }
}
